// 进行 axios 二次封装：使用请求与响应拦截器
import axios from 'axios'
import type {
  AxiosInstance,
  AxiosRequestConfig,
  InternalAxiosRequestConfig,
  AxiosError,
  CreateAxiosDefaults,
  AxiosResponse,
} from 'axios'
import type { Response } from '@/api/base/types'
import { ElMessage } from 'element-plus'
// 引入用户相关的仓库
import useUserStore from '@/stores/modules/user'

const config: CreateAxiosDefaults = {
  // 基础路径
  baseURL: import.meta.env.VITE_APP_BASE_API, // 基础路径携带 /api
  timeout: 5000, // 超时时间
}

const requestInterceptor = (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => {
  // config 配置对象, headers 属性请求头, 经常给服务器端携带公共参数
  const userStore = useUserStore()
  if (userStore.token) {
    config.headers.token = userStore.token
  }

  // 返回配置对象
  return config
}

const requestInterceptorCatch = (error: AxiosError) => {
  // 错误信息
  return Promise.reject(error)
}

const responseInterceptor = (response: AxiosResponse) => {
  // 拦截响应数据
  // 成功回调
  return response
}

const responseInterceptorCatch = (error: AxiosError) => {
  // 失败回调: 处理http网络错误
  // 定义一个变量: 存储网络错误信息
  let message = ''
  if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
    message = '请求超时'
  } else {
    // http状态码
    const status = error.response?.status
    switch (status) {
      case 401:
        message = 'Token 过期'
        break
      case 403:
        message = '无权访问'
        break
      case 404:
        message = '请求地址错误'
        break
      case 500:
        message = '服务器错误'
        break
      default:
        message = '网络出现问题'
        break
    }
  }

  // 提示错误的信息
  ElMessage({
    type: 'error',
    message: message,
  })
  return Promise.reject(error)
}

class Request {
  instance: AxiosInstance
  constructor(config: CreateAxiosDefaults) {
    // 第一步：利用 axios.create 创建一个 axios 实例(配置：基础路径、超时时间)
    this.instance = axios.create(config)

    // 第二步：request 实例添加请求拦截器
    this.instance.interceptors.request.use(requestInterceptor, requestInterceptorCatch)

    // 第三步：request 实例添加响应拦截器
    this.instance.interceptors.response.use(responseInterceptor, responseInterceptorCatch)
  }

  get<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise<T> {
    const conf = {
      method: 'get',
      url,
      params: data,
      ...config,
    }
    return this.request<T, D>(conf).then((res) => res.data)
  }

  post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    const conf = {
      method: 'post',
      url,
      data,
      ...config,
    }
    return this.request<T, D>(conf).then((res) => res.data)
  }

  put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    const conf = {
      method: 'put',
      url,
      data,
      ...config,
    }
    return this.request<T, D>(conf).then((res) => res.data)
  }

  request<T = any, D = any>(config: AxiosRequestConfig<D>): Promise<Response<T>> {
    return new Promise((resolve, reject) => {
      this.instance
        .request<T, AxiosResponse<Response<T | string>, D>, D>(config)
        .then((res) => {
          if (res.data && res.data.code === 200) {
            resolve(res.data as Response<T>)
            return
          }
          const msg = (res.data.data as string) || res.data.message || '请求失败'
          ElMessage({
            type: 'error',
            message: msg,
          })
          reject(new Error(msg))
        })
        .catch((err: Error | AxiosError) => {
          reject(new Error(err.message, { cause: err }))
        })
    })
  }
}

const request = new Request(config)

// 对外暴露
export default request
